﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics.Contracts;

namespace NetFrameworkExtensions.Mathematics
{
  public class IntContinualFraction
  {
    IEnumerator<int> data;
    List<IntRational> rationals;
    List<int> numbers;

    public IntContinualFraction(params int[] data)
      : this((IEnumerable<int>)data)
    {
      Contract.Requires(data != null);
      Contract.Requires(data.Length > 0);
    }
    public IntContinualFraction(IEnumerable<int> data)
      : this(data.GetEnumerator())
    {
      Contract.Requires(data != null);
    }
    public IntContinualFraction(IEnumerator<int> data)
    {
      Contract.Requires(data != null);

      this.data = data;
      Initialize();
    }

    public IEnumerable<IntRational> Rationals
    {
      get
      {
        for (int i = 0; ; i++)
        {
          if (i < rationals.Count)
            yield return rationals[i];
          else if (CreateNewRational())
            yield return rationals[i];
          else break;
        }
      }
    }
    public IEnumerable<int> Numbers
    {
      get
      {
        for (int i = 0; ; i++)
        {
          if (i < rationals.Count)
            yield return numbers[i];
          else if (CreateNewRational())
            yield return numbers[i];
          else break;
        }
      }
    }

    private bool CreateNewRational()
    {
      if (data == null) return false;
      if (!data.MoveNext())
      {
        data = null;
        return false;
      }
      int number = data.Current;
      int last = numbers.Count - 1;
      if (number <= 0) throw new ArgumentException("After the first integer, the rest must be all possitive.", "data");
      IntRational r;
      try
      {
        checked
        {
          if (last == 0)
            r = new IntRational(number * rationals[last].Numerator + 1, number * rationals[last].Denominator);
          else
            r = new IntRational(number * rationals[last].Numerator + rationals[last - 1].Numerator, number * rationals[last].Denominator + rationals[last - 1].Denominator);
        }
      }
      catch
      {
        data = null;
        return false;
      }
      numbers.Add(number);
      rationals.Add(r);
      return true;
    }

    private void Initialize()
    {
      if (!data.MoveNext())
        throw new ArgumentException("empty enumeration, must contain al least one element", "data");
      rationals = new List<IntRational>();
      numbers = new List<int>();
      numbers.Add(data.Current);
      rationals.Add(numbers[0]);
    }
  }
}
